package org.lemon.transport.mqtt.server.adaptors;

import com.google.gson.JsonParser;
import com.google.protobuf.Descriptors;
import com.google.protobuf.InvalidProtocolBufferException;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.mqtt.MqttMessage;
import io.netty.handler.codec.mqtt.MqttPublishMessage;
import lombok.extern.slf4j.Slf4j;
import org.lemon.transport.context.adaptor.AdaptorException;
import org.lemon.transport.context.adaptor.JsonConverter;
import org.lemon.transport.context.adaptor.ProtoConverter;
import org.lemon.transport.mqtt.server.session.DeviceSessionContext;
import org.lemon.transport.mqtt.server.session.MqttDeviceAwareSessionContext;
import org.server.transport.gen.TransportProto;

import java.util.Optional;

@Slf4j
public class ProtoMqttAdaptor implements MqttTransportAdaptor {

    @Override
    public TransportProto.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
        DeviceSessionContext deviceSessionCtx = (DeviceSessionContext) ctx;
        byte[] bytes = toBytes(inbound.payload());
        Descriptors.Descriptor telemetryDynamicMsgDescriptor = ProtoConverter.validateDescriptor(deviceSessionCtx.getTelemetryDynamicMsgDescriptor());
        try {
            return JsonConverter.convertToTelemetryProto(new JsonParser().parse(ProtoConverter.dynamicMsgToJson(bytes, telemetryDynamicMsgDescriptor)));
        } catch (Exception e) {
            log.debug("Failed to decode post telemetry request", e);
            throw new AdaptorException(e);
        }
    }

    @Override
    public TransportProto.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
        DeviceSessionContext deviceSessionCtx = (DeviceSessionContext) ctx;
        byte[] bytes = toBytes(inbound.payload());
        Descriptors.Descriptor attributesDynamicMessageDescriptor = ProtoConverter.validateDescriptor(deviceSessionCtx.getAttributesDynamicMessageDescriptor());
        try {
            return JsonConverter.convertToAttributesProto(new JsonParser().parse(ProtoConverter.dynamicMsgToJson(bytes, attributesDynamicMessageDescriptor)));
        } catch (Exception e) {
            log.debug("Failed to decode post attributes request", e);
            throw new AdaptorException(e);
        }
    }

    @Override
    public TransportProto.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
        byte[] bytes = toBytes(inbound.payload());
        try {
            return ProtoConverter.convertToProvisionRequestMsg(bytes);
        } catch (InvalidProtocolBufferException ex) {
            log.debug("Failed to decode provision request", ex);
            throw new AdaptorException(ex);
        }
    }

    @Override
    public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext deviceSessionCtx, TransportProto.ProvisionDeviceResponseMsg provisionResponseMsg) {
        return Optional.empty();
    }

    @Override
    public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext deviceSessionCtx, TransportProto.ProvisionDeviceResponseMsg provisionResponseMsg, String topicName) {
        return Optional.empty();
    }

    @Override
    public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext deviceSessionCtx, TransportProto.AttributeUpdateNotificationMsg notificationMsg, String topicName) throws AdaptorException {
        return Optional.empty();
    }

    @Override
    public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext deviceSessionCtx, TransportProto.GetAttributeResponseMsg attributesResponse, String topicName) throws AdaptorException {
        return Optional.empty();
    }


    public static byte[] toBytes(ByteBuf inbound) {
        byte[] bytes = new byte[inbound.readableBytes()];
        int readerIndex = inbound.readerIndex();
        inbound.getBytes(readerIndex, bytes);
        return bytes;
    }

    private int getRequestId(String topicName, String topic) {
        return Integer.parseInt(topicName.substring(topic.length()));
    }
}
